In this notebook, we will perform a causal analysis using
observational data from the 1985 Current Population Survey (CPS).
The goal of this analysis is to estimate the causal effect of
education on wages, taking into account potential confounding
variables.
The dataset contains information on various demographic, economic,
and labor-related variables.
CPS = read.csv("CPS1985.csv")
Exploratory Analysis
head(CPS, 10)
The dataset includes 12 variables that capture individual social and
economic characteristics. These variables can be categorized as
follows:
Numerical Variables:
wage: The wage of the individual.
education: The number of years of education.
experience: The number of years of work experience.
age: The age of the individual.
Categorical Variables:
ethnicity: The ethnicity of the individual.
region: The region where the individual lives.
gender: The gender of the individual.
occupation: The occupation of the individual.
sector: The sector of employment.
union: Union membership status.
married: Marital status.
print(colSums(is.na(CPS)))
X wage education experience age ethnicity region gender occupation sector union
0 0 0 0 0 0 0 0 0 0 0
married
0
There’s no missing values across variables, so we can proceed with
the analysis.
Let’s see the distribution of quantitative variables.
numerical_vars <- c("wage", "education", "experience", "age")
summary(CPS[numerical_vars])
wage education experience age
Min. : 1.000 Min. : 2.00 Min. : 0.00 Min. :18.00
1st Qu.: 5.250 1st Qu.:12.00 1st Qu.: 8.00 1st Qu.:28.00
Median : 7.780 Median :12.00 Median :15.00 Median :35.00
Mean : 9.024 Mean :13.02 Mean :17.82 Mean :36.83
3rd Qu.:11.250 3rd Qu.:15.00 3rd Qu.:26.00 3rd Qu.:44.00
Max. :44.500 Max. :18.00 Max. :55.00 Max. :64.00
print(paste("Sample size:", nrow(CPS)))
[1] "Sample size: 534"
We can observe that the dataset includes 534 adult people of age
ranging from 18 to 64 years, with 2 to 18 years of education and 0 to 55
years of work experience, so the variability among the observed sample
of individuals is quite large. The same applies to wages: they range
from 1 to 44.5 dollars per hour.
library(ggplot2)
library(tidyverse)
Warning: package ‘forcats’ was built under R version 4.4.1
── Attaching core tidyverse packages ───────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ lubridate 1.9.3 ✔ tibble 3.2.1
✔ purrr 1.0.2 ✔ tidyr 1.3.1
── Conflicts ─────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
# install.packages("ggiraph")
library(ggiraph)
Warning: package ‘ggiraph’ was built under R version 4.4.1
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
library(plotly)
Warning: package ‘plotly’ was built under R version 4.4.1
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
CPS_long <- CPS %>%
pivot_longer(cols = c(wage, education, experience, age), names_to = "variable_name", values_to = "value")
quantiles <- CPS_long %>%
group_by(variable_name) %>%
summarise(
Q1 = quantile(value, 0.25),
Median = quantile(value, 0.50),
Q3 = quantile(value, 0.75)
)
density_plots <- ggplot(data = CPS_long, aes(x = value)) +
geom_density(fill = "steelblue", color = "black", alpha = 0.7) +
facet_wrap(~ variable_name, scales = "free") +
labs(title = "Distribution of Numerical Variables", x = NULL, y = "Frequency") +
theme_minimal() +
geom_vline(data = quantiles, aes(xintercept = Q1), color = "blue", linetype = "dashed", size = 0.5) +
geom_vline(data = quantiles, aes(xintercept = Median), color = "red", linetype = "dashed", size = 1) +
geom_vline(data = quantiles, aes(xintercept = Q3), color = "blue", linetype = "dashed", size = 0.5)
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
ggplotly(density_plots, width = 800, height = 600)
As we can see from the plots, the distribution of wages, experience
and age is right-skewed, meaning that most individuals have lower values
for these variables. The distribution of education is more evenly
spread, having median value at around 12 years of education.
Since the variables are skewed, there might be some outliers. Let’s
have a look at the boxplots to see if there are any.
boxplots <- ggplot(CPS_long, aes(x = variable_name, y = value)) +
geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
facet_wrap(~ variable_name, scales = "free", ncol = 4) +
labs(title = "Distribution of Numerical Variables", x = NULL, y = "Value") +
theme_minimal()
ggplotly(boxplots, width = 800, height = 600)
There are visible outliers in the data:
Education: there are people who have less than 8 years of
education, which probably means they finished only a part of primary
education. The number of such observations is not high and we assume
that these are valid data points, since a small fraction of population
indeed might not finish school.
Experience: there are a couple of observations with more than 49
years of work experience (which is the 75% quantile of the data). A
older fraction of population could in fact start their careers early and
have a long work experience, so we don’t consider these observations
problematic outliers, given that the maximum age within the sample is 64
years.
Wage: there are a few observations with wages higher than 30
dollars per hour. The wage distribution normally is right-skewed, so
these observations are not problematic outliers, but rather a low number
of high earners.
Correlations between wages, education, experience, and age
Let’s now observe the corellation between the numerical variables of
interest.
numerical_vars_df <- CPS[numerical_vars]
correlation_matrix <- cor(numerical_vars_df)
correlation_matrix_long <- as.data.frame(correlation_matrix) %>%
rownames_to_column(var = "Var1") %>%
gather(key = "Var2", value = "value", -Var1) %>%
filter(Var1 != Var2) %>%
filter(!duplicated(paste(pmin(Var1, Var2), pmax(Var1, Var2))))
print(correlation_matrix_long)
The independent variable of interest - Wage - is positively
correlated with other three numerical variables with the strongest
correlation for education (~0.4). Surprisingly, years of experience and
age and not strongly correlated with Wage.
The strongest positive correlation is observable between age and work
experience, which is not suprising if we assume that almost all
individuals were working throughout their lives.
What is also interesting is that education is not strongly correlated
with experience, which might indicate that people with higher education
levels might not necessarily have more work experience (it makes sense
if we assume that individuals who were getting their education for a
longer time, might start working later).
Let’s graphically represent the relationships between wage and
education, experience, and age.
We chose to apply the Loess smoothing method to this plot because it
this method in non-perametric and does not assume a linear relationship
between the variables. Instead, Loess fits a smoothed curve that best
describes the relationship, allowing us to explore potential non-linear
patterns in the data more closely. This approach is particularly
suitable since we are uncertain about the exact trend of the
relationship.
Additionally, the dataset’s relatively small size means that
individual data points can have a significant impact. By using Loess,
which is sensitive to outliers and local variations, we can better
understand how these factors influence the overall trend between wage
and education.
scatterplot_education <- ggplot(CPS, aes(x = education, y = wage)) +
geom_point(size=1) +
geom_smooth(method="auto", se=TRUE, fullrange=FALSE, level=0.95) +
labs(title = "Education And Wage Relationship",
x = "Years of Education",
y = "Wage") +
theme_minimal()
ggplotly(scatterplot_education, width = 800, height = 600)
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'
If we consider the scatterplot of wage and education, we can observe
a positive relationship between the two variables, that was previously
observed in the correlation matrix. Furthermore, the trend is rather
consistent and the variability is not too high, which indicates that the
relationship might be quite strong. The consistently increasing upward
slope might signal that there is also no dimishing returns to more years
of educations, at least in the sectors and the occupations that we
observe in this dataset.
scatterplot_experience <- ggplot(CPS, aes(x = experience, y = wage)) +
geom_point(size=1) +
geom_smooth(method="auto", se=TRUE, fullrange=FALSE, level=0.95) +
labs(title = "Experience And Wage Relationship",
x = "Years of Experience",
y = "Wage") +
theme_minimal()
ggplotly(scatterplot_experience, width = 800, height = 600)
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'
Compared to education, work experience does not show a clear
relationship with wage: variability is so high that it makes the graph
visually noisy and without a clear distinguishable trend. This is
consistent with the correlation matrix, which showed a weaker
correlation between wage and experience compared to education.
Speaking of a trend in the fitted line - there is an upward slope
from 0 to ~12 years of experiences which is follwed by the flat line. It
might signal that the positive linearity of the relationship is not
constant across the whole career and is higher in first 10-15 years of
work experience, on average. There’s also a significant drop in wage for
individuals with more than ~40 years of experience, but the number of
observations are relatively low in this age agroup and the variability
is so high that it does not allow to make any preliminary
conclusions.
Since age is highly correlated with experience, we can expect similar
results for the relationship between wage and age on average, but the
exact trends might differ, so it might be useful to plot it as well:
scatterplot_age <- ggplot(CPS, aes(x = age, y = wage)) +
geom_point(size=1) +
geom_smooth(method="auto", se=TRUE, fullrange=FALSE, level=0.95) +
labs(title = "Experience And Wage Relationship",
x = "Age",
y = "Wage") +
theme_minimal()
ggplotly(scatterplot_age, width = 800, height = 600)
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'
As with the relationship between wage and experience, the
relationship between wage and age is not exactly linearly positive. The
fitted line shows a slight upward slope from 18 to 35 years of age,
which is followed by a stagnation. It might indicate that wage gains are
higher in first years of career, but then the wage does not grow or even
decrease a bit. It is also consistent with the trend that we observed
between wage and years of experience.
Distribution of Wages Across Different Social Groups
Let’s have a closer look at the qualitative categories that we have
in this dataset.
categorical_vars <- c("ethnicity", "region", "gender", "occupation", "sector", "union", "married")
for (var in categorical_vars) {
cat("Categories in", var, ":\n")
print(unique(CPS[[var]]))
cat("\n")
}
Categories in ethnicity :
[1] "hispanic" "cauc" "other"
Categories in region :
[1] "other" "south"
Categories in gender :
[1] "female" "male"
Categories in occupation :
[1] "worker" "management" "sales" "office" "services" "technical"
Categories in sector :
[1] "manufacturing" "other" "construction"
Categories in union :
[1] "no" "yes"
Categories in married :
[1] "yes" "no"
It might be interesting to observe differences in wage distribution
across different social groups separately.
Sectors and Occupations
Let’s start with professional occupations and sectors.
We suspect that there is a significant difference in wages among
individuals in different occupations and industries, since some sectors
and occupations require more advanced and/or specialized skills than
others.
wage_by_sector <- ggplot(CPS, aes(x = sector, y = wage)) +
geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
labs(title = "Distribution of Wages Across Sectors",
x = "Sector",
y = "Wage") +
theme_minimal()
ggplotly(wage_by_sector, width = 800, height = 600)
Median and interquartile ranges are quite close across sectors. As it
could have been expected, “other” category (which includes all data
points outside of construction and manufacturing) have the largest and
biggest outliers, but the lowest median. Construction sector has the
highest median wage and the lowest variability in wages.
summary_sectors <- CPS %>%
group_by(sector) %>%
summarise(
Mean = mean(wage),
Median = median(wage),
SD = sd(wage),
IQR = IQR(wage, na.rm = TRUE),
Count = n()
)
summary_sectors
Despite significant difference in number of observations across
sectors (“other” category is significantly larger in number of
observations), the sectors appear to be comparable in terms of mean and
median.
The standard deviation is the highest in the “other” category, which
is consistent with the boxplot. The construction sector has the lowest
standard deviation.
According to the calculated IQRs across sectors, manufacturing has
the highest variability of the middle 50% of the data, which is
consistent with the boxplot. Construction sector has the lowest
variability, which is also consistent with the boxplot.
Overall, despire rather closed measures of central tendency, the
measures of variability are quite different across sectors, which might
indicate that sector of employemtn is an important predictor of wage
level.
It is also likely to be a common cause confounder for the
relationship between education and wages, since people working in
different sectors might have different levels of education and wages. it
might be especially the case for “other” category since it includes a
wide variety of industries.
# install.packages("forcats")
library(forcats)
library(dplyr)
CPS <- CPS %>%
mutate(occupation = fct_reorder(occupation, wage, .fun = median))
wage_by_occupation <- ggplot(CPS, aes(x = occupation, y = wage)) +
geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
labs(title = "Distribution of Wages Across Occupations",
x = "Occupation",
y = "Wage") +
theme_minimal()
ggplotly(wage_by_occupation, width = 800, height = 600)
summary_occupations <- CPS %>%
group_by(occupation) %>%
summarise(
Mean = mean(wage),
Median = median(wage),
SD = sd(wage),
IQR = IQR(wage, na.rm = TRUE),
Count = n()
)
summary_occupations
Highest median and interquartile ranges are observable in the
Management and Technical groups, which means they have higher hourly
wage for a “typical” worker as well as larger variability in wages.
These two types of occupations often require advanced level education
and specialized skills, which can explain the higher wages.
Worker and Sales occupations have quite long tails with relatively
small numbers of very high earners. Successful sales specialists can
have high incomes from commissions, while some highly qualified workers
can have much higher hourly wages, than average workers due to their
high level or highly specialized skills.
Overall, there is quite high variability, average and median
differences in wages across these groups, which might indicate that
occupation is a very important predictor of wage level. It is also
likely to be a common cause confounder for the relationship
between education and wages, since people with higher education levels
might be more likely to work in higher paid sectors and occupations (it
especially holds true for management and technical groups that might
require advanced level education).
Ethnic groups
Let’s now observe the distribution of wages across different
demographic groups.
CPS <- CPS %>%
mutate(ethnicity = fct_reorder(ethnicity, wage, .fun = median))
wage_by_ethnicity <- ggplot(CPS, aes(x = ethnicity, y = wage)) +
geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
labs(title = "Distribution of Wages Across Ethnic Groups",
x = "Occupation",
y = "Wage") +
theme_minimal()
ggplotly(wage_by_ethnicity, width = 800, height = 600)
summary_ethnicity <- CPS %>%
group_by(ethnicity) %>%
summarise(
Mean = mean(wage),
Median = median(wage),
SD = sd(wage),
IQR = IQR(wage, na.rm = TRUE),
Count = n()
)
summary_ethnicity
Caucasians have both the highest median wage and the highest
variability in wages.
Higher variability can be explained by the fact that the majority of
the sample is Caucasian, so the distribution of wages is more spread
out. Additionally, there might structural differences in incomes between
different ethnic groups. For instance, non-white minorities have lower
access to higher paid sectors and occupations, which can lead to lower
wages.
library(RColorBrewer)
ethnicity_colors <- c("hispanic" = "brown4", "other" = "darkgoldenrod", "cauc" = "cadetblue")
ethnicity_occupation_plot <- ggplot(CPS, aes(x = occupation, fill = ethnicity)) +
geom_bar(position = "fill") +
labs(title = "Ethnic Composition Across Occupations",
x = "Occupation",
y = "Proportion",
fill = "Ethnicity") +
scale_y_continuous(labels = scales::percent) +
scale_fill_manual(values = ethnicity_colors) +
theme_minimal()
ggplotly(ethnicity_occupation_plot)
As we can see, among occupation groups, non-causasians’ proportions
are the highest in the office, worker and services occupation groups,
which are associated with lower wages, as we saw previously.
Generally speaking, ethnic origin might be a very important predictor
of a wage level. Additionally, it might also affect the access to
education, which means that it is a common cause
confounder.
Gender
Let’s observe the distribution of wages across gender. Gender
disparity is widely discussed in the economic literature, so it might be
interesting to see if there is a wage gap between two genders in this
dataset. We would expect a significant difference, especially since the
data comes from 80’s when gender pay gap was even more pronounced.
wage_by_gender <- ggplot(CPS, aes(x = gender, y = wage)) +
geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
labs(title = "Distribution of Wages Across Gender Groups",
x = "Gender",
y = "Wage") +
theme_minimal()
ggplotly(wage_by_gender, width = 800, height = 600)
Even though both median and interquartile ranges are higher for
males, there is quite a lot of outliers in the female groups, indicating
a small number of high earners among females.
summary_gender <- CPS %>%
group_by(gender) %>%
summarise(
Mean = mean(wage),
Median = median(wage),
SD = sd(wage),
IQR = IQR(wage, na.rm = TRUE),
Count = n()
)
summary_gender
Despite outliers mentioned earlier, the standard deviation and
interquartile range of the male group signal higher variability in
wages.
Again, we would expect gender to be a common cause
confounder for the relationship between education and wages,
because it can affect both the access to education, number of years of
formal education (due to different social norms and expectations applied
to females) and the wage level (due to glass ceilings).
Location
The data also allows to observe the differences between regions in
terms of wages. The dataset includes 2 categories related to location:
south and other. We would expect that there is a wage gap between these
two regions, since the South is generally considered to be more rural,
less developed, resulting in lower average wages.
wage_by_region <- ggplot(CPS, aes(x = region, y = wage)) +
geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
labs(title = "Distribution of Wages Across Regions",
x = "Region",
y = "Wage") +
theme_minimal()
ggplotly(wage_by_region, width = 800, height = 600)
It’s vividly visible that both the median and the range are higher in
the “other” category, which is consistent with the assumption that the
South has lower wages.
summary_region <- CPS %>%
group_by(region) %>%
summarise(
Mean = mean(wage),
Median = median(wage),
SD = sd(wage),
IQR = IQR(wage, na.rm = TRUE),
Count = n()
)
summary_region
Unionization
Unionization is another important factor that can affect wages: union
member are protected by the collective agreements and usually have
higher wages.
wage_by_union <- ggplot(CPS, aes(x = union, y = wage)) +
geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
labs(title = "Distribution of Wages Across Union Memberships",
x = "Union Membership",
y = "Wage") +
theme_minimal()
ggplotly(wage_by_union, width = 800, height = 600)
Causal Analysis
After preliminary correlation and exploratory analysis, we can now
proceed with the hypothesis testing. Education was found to have the
highest positive correlation with wages among quantitative variables
(correlations between wages and age and experience were weaker).
Qualitative variables, such as occupation and sector, also showed some
differences in wage distribution and their relationship with wages might
be interesting to explore further.
The set of hypotheses is formulated in the following way:
- Null Hypothesis (H₀): Education has no effect on
wages.
- Alternative Hypothesis (H₁): Education has an effect on
wages.
Let’s have a look at the relationship between the level of education,
controlling for experience and age, and wages.
First, since age and experience are highly correlated, we will check
for multicollinearity between these two variables, using
Variance Inflation Factor (VIF). Let’s create models with and
wothout collinear independent variables and compare the results:
# install.packages("stargazer")
library(stargazer)
Please cite as:
Hlavac, Marek (2022). stargazer: Well-Formatted Regression and Summary Statistics Tables.
R package version 5.2.3. https://CRAN.R-project.org/package=stargazer
model_numerical_1 <- lm(wage ~ education + experience + age, data = CPS)
model_numerical_2 <- lm(wage ~ education + experience, data = CPS)
model_numerical_3 <- lm(wage ~ education + age, data = CPS)
stargazer(model_numerical_1, model_numerical_2, model_numerical_3,
type = "text",
title = "Wage Vs. Education, Experience & Age",
align = TRUE,
column.labels = c("All numerical", "w/o Age", "w/o Experience"))
Wage Vs. Education, Experience & Age
===========================================================================================
Dependent variable:
-----------------------------------------------------------------------
wage
All numerical w/o Age w/o Experience
(1) (2) (3)
-------------------------------------------------------------------------------------------
education 0.948 0.926*** 0.821***
(1.155) (0.081) (0.077)
experience 0.128 0.105***
(1.156) (0.017)
age -0.022 0.105***
(1.155) (0.017)
Constant -4.770 -4.904*** -5.534***
(7.043) (1.219) (1.279)
-------------------------------------------------------------------------------------------
Observations 534 534 534
R2 0.202 0.202 0.202
Adjusted R2 0.198 0.199 0.199
Residual Std. Error 4.604 (df = 530) 4.599 (df = 531) 4.599 (df = 531)
F Statistic 44.727*** (df = 3; 530) 67.217*** (df = 2; 531) 67.210*** (df = 2; 531)
===========================================================================================
Note: *p<0.1; **p<0.05; ***p<0.01
First model includes all three variables, while the second model
includes only education and experience (excluding age). The third model
includes only education and age (excluding experience).
First model demonstrates highly inflated standard errors, most likely
due to multicollinearity between experience and age.
Both 2nd and 2rd models show and statistical and quite high
correlation between education and wage, while the respective
coefficients for isolated experience and age variables are quite
similar. it is indeed a sign of multicollinearity.
Let’s see the Variance Inflation Factor to check the power of
multicollinearity in the 1st model:
# install.packages("car")
library(car)
Warning: package ‘car’ was built under R version 4.4.1
Loading required package: carData
Warning: package ‘carData’ was built under R version 4.4.1
Attaching package: ‘car’
The following object is masked from ‘package:dplyr’:
recode
The following object is masked from ‘package:purrr’:
some
vif1 <- vif(model_numerical_1)
print(vif1)
education experience age
229.5738 5147.9190 4611.4008
The calculated VIF is extremely high, which indicates that
multicollinearity is a significant issue in the model.
Let’s check VIF for the 2nd and 3rd models:
vif2 <- vif(model_numerical_2)
print(vif2)
education experience
1.142049 1.142049
vif3 <- vif(model_numerical_3)
print(vif3)
education age
1.023024 1.023024
Both models have VIF values ~1, which is a sign of no
multicollinearity.
# install.packages("stargazer")
library(stargazer)
model_categorical <- lm(wage ~ ethnicity + region + gender + occupation + sector
+ union + married, data = CPS)
stargazer(model_categorical, type = "text", title = "Wage Vs. Categorical variables", align = TRUE)
Wage Vs. Categorical variables
================================================
Dependent variable:
---------------------------
wage
------------------------------------------------
ethnicityother 0.771
(1.023)
ethnicitycauc 1.623*
(0.893)
regionsouth -0.824*
(0.436)
gendermale 2.028***
(0.434)
occupationsales 0.761
(0.893)
occupationworker 0.188
(0.704)
occupationoffice 1.388**
(0.678)
occupationtechnical 4.752***
(0.669)
occupationmanagement 5.691***
(0.794)
sectormanufacturing 0.714
(1.033)
sectorother -0.564
(1.003)
unionyes 2.018***
(0.531)
marriedyes 0.608
(0.413)
Constant 4.392***
(1.480)
------------------------------------------------
Observations 534
R2 0.261
Adjusted R2 0.243
Residual Std. Error 4.471 (df = 520)
F Statistic 14.162*** (df = 13; 520)
================================================
Note: *p<0.1; **p<0.05; ***p<0.01
model_numerical <- lm(wage ~ education + experience + age, data = CPS)
stargazer(model_numerical, type = "text", title = "Regression Results", align = TRUE)
Regression Results
===============================================
Dependent variable:
---------------------------
wage
-----------------------------------------------
education 0.948
(1.155)
experience 0.128
(1.156)
age -0.022
(1.155)
Constant -4.770
(7.043)
-----------------------------------------------
Observations 534
R2 0.202
Adjusted R2 0.198
Residual Std. Error 4.604 (df = 530)
F Statistic 44.727*** (df = 3; 530)
===============================================
Note: *p<0.1; **p<0.05; ***p<0.01
Do outliers affect the results? - Cook’s Distance - Robustness
checks
LS0tDQp0aXRsZTogIkRvZXMgRWR1Y2F0aW9uIEFmZmVjdCBXYWdlcz8gQSBDYXVzYWwgQW5hbHlzaXMgVXNpbmcgMTk4NSBDUFMgRGF0YSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCkluIHRoaXMgbm90ZWJvb2ssIHdlIHdpbGwgcGVyZm9ybSBhIGNhdXNhbCBhbmFseXNpcyB1c2luZyBvYnNlcnZhdGlvbmFsIGRhdGEgZnJvbSB0aGUgMTk4NSBDdXJyZW50IFBvcHVsYXRpb24gU3VydmV5IChDUFMpLg0KDQpUaGUgZ29hbCBvZiB0aGlzIGFuYWx5c2lzIGlzIHRvIGVzdGltYXRlIHRoZSBjYXVzYWwgZWZmZWN0IG9mIGVkdWNhdGlvbiBvbiB3YWdlcywgdGFraW5nIGludG8gYWNjb3VudCBwb3RlbnRpYWwgY29uZm91bmRpbmcgdmFyaWFibGVzLg0KDQpUaGUgZGF0YXNldCBjb250YWlucyBpbmZvcm1hdGlvbiBvbiB2YXJpb3VzIGRlbW9ncmFwaGljLCBlY29ub21pYywgYW5kIGxhYm9yLXJlbGF0ZWQgdmFyaWFibGVzLg0KDQpgYGB7cn0NCkNQUyA9IHJlYWQuY3N2KCJDUFMxOTg1LmNzdiIpDQpgYGANCg0KIyMgRXhwbG9yYXRvcnkgQW5hbHlzaXMNCmBgYHtyfQ0KaGVhZChDUFMsIDEwKQ0KYGBgDQpUaGUgZGF0YXNldCBpbmNsdWRlcyAxMiB2YXJpYWJsZXMgdGhhdCBjYXB0dXJlIGluZGl2aWR1YWwgc29jaWFsIGFuZCBlY29ub21pYyBjaGFyYWN0ZXJpc3RpY3MuIFRoZXNlIHZhcmlhYmxlcyBjYW4gYmUgY2F0ZWdvcml6ZWQgYXMgZm9sbG93czoNCg0KKk51bWVyaWNhbCBWYXJpYWJsZXM6Kg0KDQotIHdhZ2U6IFRoZSB3YWdlIG9mIHRoZSBpbmRpdmlkdWFsLg0KDQotIGVkdWNhdGlvbjogVGhlIG51bWJlciBvZiB5ZWFycyBvZiBlZHVjYXRpb24uDQoNCi0gZXhwZXJpZW5jZTogVGhlIG51bWJlciBvZiB5ZWFycyBvZiB3b3JrIGV4cGVyaWVuY2UuDQoNCi0gYWdlOiBUaGUgYWdlIG9mIHRoZSBpbmRpdmlkdWFsLg0KDQoqQ2F0ZWdvcmljYWwgVmFyaWFibGVzOioNCg0KLSBldGhuaWNpdHk6IFRoZSBldGhuaWNpdHkgb2YgdGhlIGluZGl2aWR1YWwuDQoNCi0gcmVnaW9uOiBUaGUgcmVnaW9uIHdoZXJlIHRoZSBpbmRpdmlkdWFsIGxpdmVzLg0KDQotIGdlbmRlcjogVGhlIGdlbmRlciBvZiB0aGUgaW5kaXZpZHVhbC4NCg0KLSBvY2N1cGF0aW9uOiBUaGUgb2NjdXBhdGlvbiBvZiB0aGUgaW5kaXZpZHVhbC4NCg0KLSBzZWN0b3I6IFRoZSBzZWN0b3Igb2YgZW1wbG95bWVudC4NCg0KLSB1bmlvbjogVW5pb24gbWVtYmVyc2hpcCBzdGF0dXMuDQoNCi0gbWFycmllZDogTWFyaXRhbCBzdGF0dXMuDQoNCg0KDQpgYGB7cn0NCnByaW50KGNvbFN1bXMoaXMubmEoQ1BTKSkpDQpgYGANClRoZXJlJ3Mgbm8gbWlzc2luZyB2YWx1ZXMgYWNyb3NzIHZhcmlhYmxlcywgc28gd2UgY2FuIHByb2NlZWQgd2l0aCB0aGUgYW5hbHlzaXMuDQoNCkxldCdzIHNlZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KbnVtZXJpY2FsX3ZhcnMgPC0gYygid2FnZSIsICJlZHVjYXRpb24iLCAiZXhwZXJpZW5jZSIsICJhZ2UiKQ0Kc3VtbWFyeShDUFNbbnVtZXJpY2FsX3ZhcnNdKQ0KcHJpbnQocGFzdGUoIlNhbXBsZSBzaXplOiIsIG5yb3coQ1BTKSkpDQpgYGANCg0KV2UgY2FuIG9ic2VydmUgdGhhdCB0aGUgZGF0YXNldCBpbmNsdWRlcyA1MzQgYWR1bHQgcGVvcGxlIG9mIGFnZSByYW5naW5nIGZyb20gMTggdG8gNjQgeWVhcnMsIHdpdGggMiB0byAxOCB5ZWFycyBvZiBlZHVjYXRpb24gYW5kIDAgdG8gNTUgeWVhcnMgb2Ygd29yayBleHBlcmllbmNlLCBzbyB0aGUgdmFyaWFiaWxpdHkgYW1vbmcgdGhlIG9ic2VydmVkIHNhbXBsZSBvZiBpbmRpdmlkdWFscyBpcyBxdWl0ZSBsYXJnZS4gVGhlIHNhbWUgYXBwbGllcyB0byB3YWdlczogdGhleSByYW5nZSBmcm9tIDEgdG8gNDQuNSBkb2xsYXJzIHBlciBob3VyLg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ2lyYXBoIikNCmxpYnJhcnkoZ2dpcmFwaCkNCmxpYnJhcnkocGxvdGx5KQ0KDQpDUFNfbG9uZyA8LSBDUFMgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gYyh3YWdlLCBlZHVjYXRpb24sIGV4cGVyaWVuY2UsIGFnZSksIG5hbWVzX3RvID0gInZhcmlhYmxlX25hbWUiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKQ0KDQpxdWFudGlsZXMgPC0gQ1BTX2xvbmcgJT4lDQogIGdyb3VwX2J5KHZhcmlhYmxlX25hbWUpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgUTEgPSBxdWFudGlsZSh2YWx1ZSwgMC4yNSksDQogICAgTWVkaWFuID0gcXVhbnRpbGUodmFsdWUsIDAuNTApLA0KICAgIFEzID0gcXVhbnRpbGUodmFsdWUsIDAuNzUpDQogICkNCg0KZGVuc2l0eV9wbG90cyA8LSBnZ3Bsb3QoZGF0YSA9IENQU19sb25nLCBhZXMoeCA9IHZhbHVlKSkgKw0KICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJzdGVlbGJsdWUiLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC43KSArDQogIGZhY2V0X3dyYXAofiB2YXJpYWJsZV9uYW1lLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgTnVtZXJpY2FsIFZhcmlhYmxlcyIsIHggPSBOVUxMLCB5ID0gIkZyZXF1ZW5jeSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgZ2VvbV92bGluZShkYXRhID0gcXVhbnRpbGVzLCBhZXMoeGludGVyY2VwdCA9IFExKSwgY29sb3IgPSAiYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAwLjUpICsNCiAgZ2VvbV92bGluZShkYXRhID0gcXVhbnRpbGVzLCBhZXMoeGludGVyY2VwdCA9IE1lZGlhbiksIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArDQogIGdlb21fdmxpbmUoZGF0YSA9IHF1YW50aWxlcywgYWVzKHhpbnRlcmNlcHQgPSBRMyksIGNvbG9yID0gImJsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMC41KQ0KDQpnZ3Bsb3RseShkZW5zaXR5X3Bsb3RzLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQ0KYGBgDQpBcyB3ZSBjYW4gc2VlIGZyb20gdGhlIHBsb3RzLCB0aGUgZGlzdHJpYnV0aW9uIG9mIHdhZ2VzLCBleHBlcmllbmNlIGFuZCBhZ2UgaXMgcmlnaHQtc2tld2VkLCBtZWFuaW5nIHRoYXQgbW9zdCBpbmRpdmlkdWFscyBoYXZlIGxvd2VyIHZhbHVlcyBmb3IgdGhlc2UgdmFyaWFibGVzLiBUaGUgZGlzdHJpYnV0aW9uIG9mIGVkdWNhdGlvbiBpcyBtb3JlIGV2ZW5seSBzcHJlYWQsIGhhdmluZyBtZWRpYW4gdmFsdWUgYXQgYXJvdW5kIDEyIHllYXJzIG9mIGVkdWNhdGlvbi4NCg0KU2luY2UgdGhlIHZhcmlhYmxlcyBhcmUgc2tld2VkLCB0aGVyZSBtaWdodCBiZSBzb21lIG91dGxpZXJzLiBMZXQncyBoYXZlIGEgbG9vayBhdCB0aGUgYm94cGxvdHMgdG8gc2VlIGlmIHRoZXJlIGFyZSBhbnkuDQoNCmBgYHtyfQ0KYm94cGxvdHMgPC0gZ2dwbG90KENQU19sb25nLCBhZXMoeCA9IHZhcmlhYmxlX25hbWUsIHkgPSB2YWx1ZSkpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAic3RlZWxibHVlIiwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNykgKw0KICBmYWNldF93cmFwKH4gdmFyaWFibGVfbmFtZSwgc2NhbGVzID0gImZyZWUiLCBuY29sID0gNCkgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBOdW1lcmljYWwgVmFyaWFibGVzIiwgeCA9IE5VTEwsIHkgPSAiVmFsdWUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpnZ3Bsb3RseShib3hwbG90cywgd2lkdGggPSA4MDAsIGhlaWdodCA9IDYwMCkNCmBgYA0KDQpUaGVyZSBhcmUgdmlzaWJsZSBvdXRsaWVycyBpbiB0aGUgZGF0YToNCg0KLSBFZHVjYXRpb246IHRoZXJlIGFyZSBwZW9wbGUgd2hvIGhhdmUgbGVzcyB0aGFuIDggeWVhcnMgb2YgZWR1Y2F0aW9uLCB3aGljaCBwcm9iYWJseSBtZWFucyB0aGV5IGZpbmlzaGVkIG9ubHkgYSBwYXJ0IG9mIHByaW1hcnkgZWR1Y2F0aW9uLiBUaGUgbnVtYmVyIG9mIHN1Y2ggb2JzZXJ2YXRpb25zIGlzIG5vdCBoaWdoIGFuZCB3ZSBhc3N1bWUgdGhhdCB0aGVzZSBhcmUgdmFsaWQgZGF0YSBwb2ludHMsIHNpbmNlIGEgc21hbGwgZnJhY3Rpb24gb2YgcG9wdWxhdGlvbiBpbmRlZWQgbWlnaHQgbm90IGZpbmlzaCBzY2hvb2wuDQoNCi0gRXhwZXJpZW5jZTogdGhlcmUgYXJlIGEgY291cGxlIG9mIG9ic2VydmF0aW9ucyB3aXRoIG1vcmUgdGhhbiA0OSB5ZWFycyBvZiB3b3JrIGV4cGVyaWVuY2UgKHdoaWNoIGlzIHRoZSA3NSUgcXVhbnRpbGUgb2YgdGhlIGRhdGEpLiBBIG9sZGVyIGZyYWN0aW9uIG9mIHBvcHVsYXRpb24gY291bGQgaW4gZmFjdCBzdGFydCB0aGVpciBjYXJlZXJzIGVhcmx5IGFuZCBoYXZlIGEgbG9uZyB3b3JrIGV4cGVyaWVuY2UsIHNvIHdlIGRvbid0IGNvbnNpZGVyIHRoZXNlIG9ic2VydmF0aW9ucyBwcm9ibGVtYXRpYyBvdXRsaWVycywgZ2l2ZW4gdGhhdCB0aGUgbWF4aW11bSBhZ2Ugd2l0aGluIHRoZSBzYW1wbGUgaXMgNjQgeWVhcnMuDQoNCi0gV2FnZTogdGhlcmUgYXJlIGEgZmV3IG9ic2VydmF0aW9ucyB3aXRoIHdhZ2VzIGhpZ2hlciB0aGFuIDMwIGRvbGxhcnMgcGVyIGhvdXIuIFRoZSB3YWdlIGRpc3RyaWJ1dGlvbiBub3JtYWxseSBpcyByaWdodC1za2V3ZWQsIHNvIHRoZXNlIG9ic2VydmF0aW9ucyBhcmUgbm90IHByb2JsZW1hdGljIG91dGxpZXJzLCBidXQgcmF0aGVyIGEgbG93IG51bWJlciBvZiBoaWdoIGVhcm5lcnMuDQoNCg0KIyMgQ29ycmVsYXRpb25zIGJldHdlZW4gd2FnZXMsIGVkdWNhdGlvbiwgZXhwZXJpZW5jZSwgYW5kIGFnZQ0KDQpMZXQncyBub3cgb2JzZXJ2ZSB0aGUgY29yZWxsYXRpb24gYmV0d2VlbiB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcyBvZiBpbnRlcmVzdC4NCg0KYGBge3J9DQpudW1lcmljYWxfdmFyc19kZiA8LSBDUFNbbnVtZXJpY2FsX3ZhcnNdDQoNCmNvcnJlbGF0aW9uX21hdHJpeCA8LSBjb3IobnVtZXJpY2FsX3ZhcnNfZGYpDQoNCmNvcnJlbGF0aW9uX21hdHJpeF9sb25nIDwtIGFzLmRhdGEuZnJhbWUoY29ycmVsYXRpb25fbWF0cml4KSAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJWYXIxIikgJT4lDQogIGdhdGhlcihrZXkgPSAiVmFyMiIsIHZhbHVlID0gInZhbHVlIiwgLVZhcjEpICU+JQ0KICBmaWx0ZXIoVmFyMSAhPSBWYXIyKSAlPiUNCiAgZmlsdGVyKCFkdXBsaWNhdGVkKHBhc3RlKHBtaW4oVmFyMSwgVmFyMiksIHBtYXgoVmFyMSwgVmFyMikpKSkNCg0KDQpwcmludChjb3JyZWxhdGlvbl9tYXRyaXhfbG9uZykNCmBgYA0KVGhlIGluZGVwZW5kZW50IHZhcmlhYmxlIG9mIGludGVyZXN0IC0gV2FnZSAtIGlzIHBvc2l0aXZlbHkgY29ycmVsYXRlZCB3aXRoIG90aGVyIHRocmVlIG51bWVyaWNhbCB2YXJpYWJsZXMgd2l0aCB0aGUgc3Ryb25nZXN0IGNvcnJlbGF0aW9uIGZvciBlZHVjYXRpb24gKH4wLjQpLiBTdXJwcmlzaW5nbHksIHllYXJzIG9mIGV4cGVyaWVuY2UgYW5kIGFnZSBhbmQgbm90IHN0cm9uZ2x5IGNvcnJlbGF0ZWQgd2l0aCBXYWdlLg0KDQpUaGUgc3Ryb25nZXN0IHBvc2l0aXZlIGNvcnJlbGF0aW9uIGlzIG9ic2VydmFibGUgYmV0d2VlbiBhZ2UgYW5kIHdvcmsgZXhwZXJpZW5jZSwgd2hpY2ggaXMgbm90IHN1cHJpc2luZyBpZiB3ZSBhc3N1bWUgdGhhdCBhbG1vc3QgYWxsIGluZGl2aWR1YWxzIHdlcmUgd29ya2luZyB0aHJvdWdob3V0IHRoZWlyIGxpdmVzLg0KDQpXaGF0IGlzIGFsc28gaW50ZXJlc3RpbmcgaXMgdGhhdCBlZHVjYXRpb24gaXMgbm90IHN0cm9uZ2x5IGNvcnJlbGF0ZWQgd2l0aCBleHBlcmllbmNlLCB3aGljaCBtaWdodCBpbmRpY2F0ZSB0aGF0IHBlb3BsZSB3aXRoIGhpZ2hlciBlZHVjYXRpb24gbGV2ZWxzIG1pZ2h0IG5vdCBuZWNlc3NhcmlseSBoYXZlIG1vcmUgd29yayBleHBlcmllbmNlIChpdCBtYWtlcyBzZW5zZSBpZiB3ZSBhc3N1bWUgdGhhdCBpbmRpdmlkdWFscyB3aG8gd2VyZSBnZXR0aW5nIHRoZWlyIGVkdWNhdGlvbiBmb3IgYSBsb25nZXIgdGltZSwgbWlnaHQgc3RhcnQgd29ya2luZyBsYXRlcikuDQoNCkxldCdzIGdyYXBoaWNhbGx5IHJlcHJlc2VudCB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHdhZ2UgYW5kIGVkdWNhdGlvbiwgZXhwZXJpZW5jZSwgYW5kIGFnZS4NCg0KV2UgY2hvc2UgdG8gYXBwbHkgdGhlIExvZXNzIHNtb290aGluZyBtZXRob2QgdG8gdGhpcyBwbG90IGJlY2F1c2UgaXQgdGhpcyBtZXRob2QgaW4gbm9uLXBlcmFtZXRyaWMgYW5kIGRvZXMgbm90IGFzc3VtZSBhIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdmFyaWFibGVzLiBJbnN0ZWFkLCBMb2VzcyBmaXRzIGEgc21vb3RoZWQgY3VydmUgdGhhdCBiZXN0IGRlc2NyaWJlcyB0aGUgcmVsYXRpb25zaGlwLCBhbGxvd2luZyB1cyB0byBleHBsb3JlIHBvdGVudGlhbCBub24tbGluZWFyIHBhdHRlcm5zIGluIHRoZSBkYXRhIG1vcmUgY2xvc2VseS4gVGhpcyBhcHByb2FjaCBpcyBwYXJ0aWN1bGFybHkgc3VpdGFibGUgc2luY2Ugd2UgYXJlIHVuY2VydGFpbiBhYm91dCB0aGUgZXhhY3QgdHJlbmQgb2YgdGhlIHJlbGF0aW9uc2hpcC4NCg0KQWRkaXRpb25hbGx5LCB0aGUgZGF0YXNldCdzIHJlbGF0aXZlbHkgc21hbGwgc2l6ZSBtZWFucyB0aGF0IGluZGl2aWR1YWwgZGF0YSBwb2ludHMgY2FuIGhhdmUgYSBzaWduaWZpY2FudCBpbXBhY3QuIEJ5IHVzaW5nIExvZXNzLCB3aGljaCBpcyBzZW5zaXRpdmUgdG8gb3V0bGllcnMgYW5kIGxvY2FsIHZhcmlhdGlvbnMsIHdlIGNhbiBiZXR0ZXIgdW5kZXJzdGFuZCBob3cgdGhlc2UgZmFjdG9ycyBpbmZsdWVuY2UgdGhlIG92ZXJhbGwgdHJlbmQgYmV0d2VlbiB3YWdlIGFuZCBlZHVjYXRpb24uDQoNCmBgYHtyfQ0Kc2NhdHRlcnBsb3RfZWR1Y2F0aW9uIDwtIGdncGxvdChDUFMsIGFlcyh4ID0gZWR1Y2F0aW9uLCB5ID0gd2FnZSkpICsNCiAgZ2VvbV9wb2ludChzaXplPTEpICsNCiBnZW9tX3Ntb290aChtZXRob2Q9ImF1dG8iLCBzZT1UUlVFLCBmdWxscmFuZ2U9RkFMU0UsIGxldmVsPTAuOTUpICsNCiAgbGFicyh0aXRsZSA9ICJFZHVjYXRpb24gQW5kIFdhZ2UgUmVsYXRpb25zaGlwIiwNCiAgICAgICB4ID0gIlllYXJzIG9mIEVkdWNhdGlvbiIsDQogICAgICAgeSA9ICJXYWdlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90bHkoc2NhdHRlcnBsb3RfZWR1Y2F0aW9uLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQ0KYGBgDQoNCklmIHdlIGNvbnNpZGVyIHRoZSBzY2F0dGVycGxvdCBvZiB3YWdlIGFuZCBlZHVjYXRpb24sIHdlIGNhbiBvYnNlcnZlIGEgcG9zaXRpdmUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMsIHRoYXQgd2FzIHByZXZpb3VzbHkgb2JzZXJ2ZWQgaW4gdGhlIGNvcnJlbGF0aW9uIG1hdHJpeC4gRnVydGhlcm1vcmUsIHRoZSB0cmVuZCBpcyByYXRoZXIgY29uc2lzdGVudCBhbmQgdGhlIHZhcmlhYmlsaXR5IGlzIG5vdCB0b28gaGlnaCwgd2hpY2ggaW5kaWNhdGVzIHRoYXQgdGhlIHJlbGF0aW9uc2hpcCBtaWdodCBiZSBxdWl0ZSBzdHJvbmcuIFRoZSBjb25zaXN0ZW50bHkgaW5jcmVhc2luZyB1cHdhcmQgc2xvcGUgbWlnaHQgc2lnbmFsIHRoYXQgdGhlcmUgaXMgYWxzbyBubyBkaW1pc2hpbmcgcmV0dXJucyB0byBtb3JlIHllYXJzIG9mIGVkdWNhdGlvbnMsIGF0IGxlYXN0IGluIHRoZSBzZWN0b3JzIGFuZCB0aGUgb2NjdXBhdGlvbnMgdGhhdCB3ZSBvYnNlcnZlIGluIHRoaXMgZGF0YXNldC4gDQoNCmBgYHtyfQ0Kc2NhdHRlcnBsb3RfZXhwZXJpZW5jZSA8LSBnZ3Bsb3QoQ1BTLCBhZXMoeCA9IGV4cGVyaWVuY2UsIHkgPSB3YWdlKSkgKw0KICBnZW9tX3BvaW50KHNpemU9MSkgKw0KIGdlb21fc21vb3RoKG1ldGhvZD0iYXV0byIsIHNlPVRSVUUsIGZ1bGxyYW5nZT1GQUxTRSwgbGV2ZWw9MC45NSkgKw0KICBsYWJzKHRpdGxlID0gIkV4cGVyaWVuY2UgQW5kIFdhZ2UgUmVsYXRpb25zaGlwIiwNCiAgICAgICB4ID0gIlllYXJzIG9mIEV4cGVyaWVuY2UiLA0KICAgICAgIHkgPSAiV2FnZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdGx5KHNjYXR0ZXJwbG90X2V4cGVyaWVuY2UsIHdpZHRoID0gODAwLCBoZWlnaHQgPSA2MDApDQpgYGANCg0KQ29tcGFyZWQgdG8gZWR1Y2F0aW9uLCB3b3JrIGV4cGVyaWVuY2UgZG9lcyBub3Qgc2hvdyBhIGNsZWFyIHJlbGF0aW9uc2hpcCB3aXRoIHdhZ2U6IHZhcmlhYmlsaXR5IGlzIHNvIGhpZ2ggdGhhdCBpdCBtYWtlcyB0aGUgZ3JhcGggdmlzdWFsbHkgbm9pc3kgYW5kIHdpdGhvdXQgYSBjbGVhciBkaXN0aW5ndWlzaGFibGUgdHJlbmQuIFRoaXMgaXMgY29uc2lzdGVudCB3aXRoIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXgsIHdoaWNoIHNob3dlZCBhIHdlYWtlciBjb3JyZWxhdGlvbiBiZXR3ZWVuIHdhZ2UgYW5kIGV4cGVyaWVuY2UgY29tcGFyZWQgdG8gZWR1Y2F0aW9uLg0KDQpTcGVha2luZyBvZiBhIHRyZW5kIGluIHRoZSBmaXR0ZWQgbGluZSAtIHRoZXJlIGlzIGFuIHVwd2FyZCBzbG9wZSBmcm9tIDAgdG8gfjEyIHllYXJzIG9mIGV4cGVyaWVuY2VzIHdoaWNoIGlzIGZvbGx3ZWQgYnkgdGhlIGZsYXQgbGluZS4gSXQgbWlnaHQgc2lnbmFsIHRoYXQgdGhlIHBvc2l0aXZlIGxpbmVhcml0eSBvZiB0aGUgcmVsYXRpb25zaGlwIGlzIG5vdCBjb25zdGFudCBhY3Jvc3MgdGhlIHdob2xlIGNhcmVlciBhbmQgaXMgaGlnaGVyIGluIGZpcnN0IDEwLTE1IHllYXJzIG9mIHdvcmsgZXhwZXJpZW5jZSwgb24gYXZlcmFnZS4gVGhlcmUncyBhbHNvIGEgc2lnbmlmaWNhbnQgZHJvcCBpbiB3YWdlIGZvciBpbmRpdmlkdWFscyB3aXRoIG1vcmUgdGhhbiB+NDAgeWVhcnMgb2YgZXhwZXJpZW5jZSwgYnV0IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGFyZSByZWxhdGl2ZWx5IGxvdyBpbiB0aGlzIGFnZSBhZ3JvdXAgYW5kIHRoZSB2YXJpYWJpbGl0eSBpcyBzbyBoaWdoIHRoYXQgaXQgZG9lcyBub3QgYWxsb3cgdG8gbWFrZSBhbnkgcHJlbGltaW5hcnkgY29uY2x1c2lvbnMuDQoNClNpbmNlIGFnZSBpcyBoaWdobHkgY29ycmVsYXRlZCB3aXRoIGV4cGVyaWVuY2UsIHdlIGNhbiBleHBlY3Qgc2ltaWxhciByZXN1bHRzIGZvciB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gd2FnZSBhbmQgYWdlIG9uIGF2ZXJhZ2UsIGJ1dCB0aGUgZXhhY3QgdHJlbmRzIG1pZ2h0IGRpZmZlciwgc28gaXQgbWlnaHQgYmUgdXNlZnVsIHRvIHBsb3QgaXQgYXMgd2VsbDoNCg0KYGBge3J9DQpzY2F0dGVycGxvdF9hZ2UgPC0gZ2dwbG90KENQUywgYWVzKHggPSBhZ2UsIHkgPSB3YWdlKSkgKw0KICBnZW9tX3BvaW50KHNpemU9MSkgKw0KIGdlb21fc21vb3RoKG1ldGhvZD0iYXV0byIsIHNlPVRSVUUsIGZ1bGxyYW5nZT1GQUxTRSwgbGV2ZWw9MC45NSkgKw0KICBsYWJzKHRpdGxlID0gIkV4cGVyaWVuY2UgQW5kIFdhZ2UgUmVsYXRpb25zaGlwIiwNCiAgICAgICB4ID0gIkFnZSIsDQogICAgICAgeSA9ICJXYWdlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90bHkoc2NhdHRlcnBsb3RfYWdlLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQ0KYGBgDQpBcyB3aXRoIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB3YWdlIGFuZCBleHBlcmllbmNlLCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gd2FnZSBhbmQgYWdlIGlzIG5vdCBleGFjdGx5IGxpbmVhcmx5IHBvc2l0aXZlLiBUaGUgZml0dGVkIGxpbmUgc2hvd3MgYSBzbGlnaHQgdXB3YXJkIHNsb3BlIGZyb20gMTggdG8gMzUgeWVhcnMgb2YgYWdlLCB3aGljaCBpcyBmb2xsb3dlZCBieSBhIHN0YWduYXRpb24uIEl0IG1pZ2h0IGluZGljYXRlIHRoYXQgd2FnZSBnYWlucyBhcmUgaGlnaGVyIGluIGZpcnN0IHllYXJzIG9mIGNhcmVlciwgYnV0IHRoZW4gdGhlIHdhZ2UgZG9lcyBub3QgZ3JvdyBvciBldmVuIGRlY3JlYXNlIGEgYml0LiBJdCBpcyBhbHNvIGNvbnNpc3RlbnQgd2l0aCB0aGUgdHJlbmQgdGhhdCB3ZSBvYnNlcnZlZCBiZXR3ZWVuIHdhZ2UgYW5kIHllYXJzIG9mIGV4cGVyaWVuY2UuDQoNCiMjIERpc3RyaWJ1dGlvbiBvZiBXYWdlcyBBY3Jvc3MgRGlmZmVyZW50IFNvY2lhbCBHcm91cHMNCg0KTGV0J3MgaGF2ZSBhIGNsb3NlciBsb29rIGF0IHRoZSBxdWFsaXRhdGl2ZSBjYXRlZ29yaWVzIHRoYXQgd2UgaGF2ZSBpbiB0aGlzIGRhdGFzZXQuDQoNCmBgYHtyfQ0KY2F0ZWdvcmljYWxfdmFycyA8LSBjKCJldGhuaWNpdHkiLCAicmVnaW9uIiwgImdlbmRlciIsICJvY2N1cGF0aW9uIiwgInNlY3RvciIsICJ1bmlvbiIsICJtYXJyaWVkIikNCg0KZm9yICh2YXIgaW4gY2F0ZWdvcmljYWxfdmFycykgew0KICBjYXQoIkNhdGVnb3JpZXMgaW4iLCB2YXIsICI6XG4iKQ0KICBwcmludCh1bmlxdWUoQ1BTW1t2YXJdXSkpDQogIGNhdCgiXG4iKQ0KfQ0KYGBgDQpJdCBtaWdodCBiZSBpbnRlcmVzdGluZyB0byBvYnNlcnZlIGRpZmZlcmVuY2VzIGluIHdhZ2UgZGlzdHJpYnV0aW9uIGFjcm9zcyBkaWZmZXJlbnQgc29jaWFsIGdyb3VwcyBzZXBhcmF0ZWx5Lg0KDQoqKlNlY3RvcnMgYW5kIE9jY3VwYXRpb25zKioNCg0KTGV0J3Mgc3RhcnQgd2l0aCBwcm9mZXNzaW9uYWwgb2NjdXBhdGlvbnMgYW5kIHNlY3RvcnMuDQoNCldlIHN1c3BlY3QgdGhhdCB0aGVyZSBpcyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gd2FnZXMgYW1vbmcgaW5kaXZpZHVhbHMgaW4gZGlmZmVyZW50IG9jY3VwYXRpb25zIGFuZCBpbmR1c3RyaWVzLCBzaW5jZSBzb21lIHNlY3RvcnMgYW5kIG9jY3VwYXRpb25zIHJlcXVpcmUgbW9yZSBhZHZhbmNlZCBhbmQvb3Igc3BlY2lhbGl6ZWQgc2tpbGxzIHRoYW4gb3RoZXJzLg0KDQpgYGB7cn0NCndhZ2VfYnlfc2VjdG9yIDwtIGdncGxvdChDUFMsIGFlcyh4ID0gc2VjdG9yLCB5ID0gd2FnZSkpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAic3RlZWxibHVlIiwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNykgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBXYWdlcyBBY3Jvc3MgU2VjdG9ycyIsDQogICAgICAgeCA9ICJTZWN0b3IiLA0KICAgICAgIHkgPSAiV2FnZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdGx5KHdhZ2VfYnlfc2VjdG9yLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQ0KYGBgDQpNZWRpYW4gYW5kIGludGVycXVhcnRpbGUgcmFuZ2VzIGFyZSBxdWl0ZSBjbG9zZSBhY3Jvc3Mgc2VjdG9ycy4gQXMgaXQgY291bGQgaGF2ZSBiZWVuIGV4cGVjdGVkLCAib3RoZXIiIGNhdGVnb3J5ICh3aGljaCBpbmNsdWRlcyBhbGwgZGF0YSBwb2ludHMgb3V0c2lkZSBvZiBjb25zdHJ1Y3Rpb24gYW5kIG1hbnVmYWN0dXJpbmcpIGhhdmUgdGhlIGxhcmdlc3QgYW5kIGJpZ2dlc3Qgb3V0bGllcnMsIGJ1dCB0aGUgbG93ZXN0IG1lZGlhbi4gQ29uc3RydWN0aW9uIHNlY3RvciBoYXMgdGhlIGhpZ2hlc3QgbWVkaWFuIHdhZ2UgYW5kIHRoZSBsb3dlc3QgdmFyaWFiaWxpdHkgaW4gd2FnZXMuDQoNCmBgYHtyfQ0Kc3VtbWFyeV9zZWN0b3JzIDwtIENQUyAlPiUNCiAgZ3JvdXBfYnkoc2VjdG9yKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE1lYW4gPSBtZWFuKHdhZ2UpLA0KICAgIE1lZGlhbiA9IG1lZGlhbih3YWdlKSwNCiAgICBTRCA9IHNkKHdhZ2UpLA0KICAgIElRUiA9IElRUih3YWdlLCBuYS5ybSA9IFRSVUUpLA0KICAgIENvdW50ID0gbigpDQogICkNCg0Kc3VtbWFyeV9zZWN0b3JzDQpgYGANCkRlc3BpdGUgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGFjcm9zcyBzZWN0b3JzICgib3RoZXIiIGNhdGVnb3J5IGlzIHNpZ25pZmljYW50bHkgbGFyZ2VyIGluIG51bWJlciBvZiBvYnNlcnZhdGlvbnMpLCB0aGUgc2VjdG9ycyBhcHBlYXIgdG8gYmUgY29tcGFyYWJsZSBpbiB0ZXJtcyBvZiBtZWFuIGFuZCBtZWRpYW4uDQoNClRoZSBzdGFuZGFyZCBkZXZpYXRpb24gaXMgdGhlIGhpZ2hlc3QgaW4gdGhlICJvdGhlciIgY2F0ZWdvcnksIHdoaWNoIGlzIGNvbnNpc3RlbnQgd2l0aCB0aGUgYm94cGxvdC4gVGhlIGNvbnN0cnVjdGlvbiBzZWN0b3IgaGFzIHRoZSBsb3dlc3Qgc3RhbmRhcmQgZGV2aWF0aW9uLg0KDQpBY2NvcmRpbmcgdG8gdGhlIGNhbGN1bGF0ZWQgSVFScyBhY3Jvc3Mgc2VjdG9ycywgbWFudWZhY3R1cmluZyBoYXMgdGhlIGhpZ2hlc3QgdmFyaWFiaWxpdHkgb2YgdGhlIG1pZGRsZSA1MCUgb2YgdGhlIGRhdGEsIHdoaWNoIGlzIGNvbnNpc3RlbnQgd2l0aCB0aGUgYm94cGxvdC4gQ29uc3RydWN0aW9uIHNlY3RvciBoYXMgdGhlIGxvd2VzdCB2YXJpYWJpbGl0eSwgd2hpY2ggaXMgYWxzbyBjb25zaXN0ZW50IHdpdGggdGhlIGJveHBsb3QuDQoNCk92ZXJhbGwsIGRlc3BpcmUgcmF0aGVyIGNsb3NlZCBtZWFzdXJlcyBvZiBjZW50cmFsIHRlbmRlbmN5LCB0aGUgbWVhc3VyZXMgb2YgdmFyaWFiaWxpdHkgYXJlIHF1aXRlIGRpZmZlcmVudCBhY3Jvc3Mgc2VjdG9ycywgd2hpY2ggbWlnaHQgaW5kaWNhdGUgdGhhdCBzZWN0b3Igb2YgZW1wbG95ZW10biBpcyBhbiBpbXBvcnRhbnQgcHJlZGljdG9yIG9mIHdhZ2UgbGV2ZWwuDQoNCkl0IGlzIGFsc28gbGlrZWx5IHRvIGJlIGEgKmNvbW1vbiBjYXVzZSBjb25mb3VuZGVyKiBmb3IgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGVkdWNhdGlvbiBhbmQgd2FnZXMsIHNpbmNlIHBlb3BsZSB3b3JraW5nIGluIGRpZmZlcmVudCBzZWN0b3JzIG1pZ2h0IGhhdmUgZGlmZmVyZW50IGxldmVscyBvZiBlZHVjYXRpb24gYW5kIHdhZ2VzLiBpdCBtaWdodCBiZSBlc3BlY2lhbGx5IHRoZSBjYXNlIGZvciAib3RoZXIiIGNhdGVnb3J5IHNpbmNlIGl0IGluY2x1ZGVzIGEgd2lkZSB2YXJpZXR5IG9mIGluZHVzdHJpZXMuDQoNCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoImZvcmNhdHMiKQ0KbGlicmFyeShmb3JjYXRzKQ0KbGlicmFyeShkcGx5cikNCg0KQ1BTIDwtIENQUyAlPiUNCiAgbXV0YXRlKG9jY3VwYXRpb24gPSBmY3RfcmVvcmRlcihvY2N1cGF0aW9uLCB3YWdlLCAuZnVuID0gbWVkaWFuKSkNCg0Kd2FnZV9ieV9vY2N1cGF0aW9uIDwtIGdncGxvdChDUFMsIGFlcyh4ID0gb2NjdXBhdGlvbiwgeSA9IHdhZ2UpKSArDQogIGdlb21fYm94cGxvdChmaWxsID0gInN0ZWVsYmx1ZSIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjcpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgV2FnZXMgQWNyb3NzIE9jY3VwYXRpb25zIiwNCiAgICAgICB4ID0gIk9jY3VwYXRpb24iLA0KICAgICAgIHkgPSAiV2FnZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdGx5KHdhZ2VfYnlfb2NjdXBhdGlvbiwgd2lkdGggPSA4MDAsIGhlaWdodCA9IDYwMCkNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnlfb2NjdXBhdGlvbnMgPC0gQ1BTICU+JQ0KICBncm91cF9ieShvY2N1cGF0aW9uKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE1lYW4gPSBtZWFuKHdhZ2UpLA0KICAgIE1lZGlhbiA9IG1lZGlhbih3YWdlKSwNCiAgICBTRCA9IHNkKHdhZ2UpLA0KICAgIElRUiA9IElRUih3YWdlLCBuYS5ybSA9IFRSVUUpLA0KICAgIENvdW50ID0gbigpDQogICkNCg0Kc3VtbWFyeV9vY2N1cGF0aW9ucw0KYGBgDQpIaWdoZXN0IG1lZGlhbiBhbmQgaW50ZXJxdWFydGlsZSByYW5nZXMgYXJlIG9ic2VydmFibGUgaW4gdGhlIE1hbmFnZW1lbnQgYW5kIFRlY2huaWNhbCBncm91cHMsIHdoaWNoIG1lYW5zIHRoZXkgaGF2ZSBoaWdoZXIgaG91cmx5IHdhZ2UgZm9yIGEgInR5cGljYWwiIHdvcmtlciBhcyB3ZWxsIGFzIGxhcmdlciB2YXJpYWJpbGl0eSBpbiB3YWdlcy4gVGhlc2UgdHdvIHR5cGVzIG9mIG9jY3VwYXRpb25zIG9mdGVuIHJlcXVpcmUgYWR2YW5jZWQgbGV2ZWwgZWR1Y2F0aW9uIGFuZCBzcGVjaWFsaXplZCBza2lsbHMsIHdoaWNoIGNhbiBleHBsYWluIHRoZSBoaWdoZXIgd2FnZXMuDQoNCldvcmtlciBhbmQgU2FsZXMgb2NjdXBhdGlvbnMgaGF2ZSBxdWl0ZSBsb25nIHRhaWxzIHdpdGggcmVsYXRpdmVseSBzbWFsbCBudW1iZXJzIG9mIHZlcnkgaGlnaCBlYXJuZXJzLiBTdWNjZXNzZnVsIHNhbGVzIHNwZWNpYWxpc3RzIGNhbiBoYXZlIGhpZ2ggaW5jb21lcyBmcm9tIGNvbW1pc3Npb25zLCB3aGlsZSBzb21lIGhpZ2hseSBxdWFsaWZpZWQgd29ya2VycyBjYW4gaGF2ZSBtdWNoIGhpZ2hlciBob3VybHkgd2FnZXMsIHRoYW4gYXZlcmFnZSB3b3JrZXJzIGR1ZSB0byB0aGVpciBoaWdoIGxldmVsIG9yIGhpZ2hseSBzcGVjaWFsaXplZCBza2lsbHMuDQoNCk92ZXJhbGwsIHRoZXJlIGlzIHF1aXRlIGhpZ2ggdmFyaWFiaWxpdHksIGF2ZXJhZ2UgYW5kIG1lZGlhbiBkaWZmZXJlbmNlcyBpbiB3YWdlcyBhY3Jvc3MgdGhlc2UgZ3JvdXBzLCB3aGljaCBtaWdodCBpbmRpY2F0ZSB0aGF0IG9jY3VwYXRpb24gaXMgYSB2ZXJ5IGltcG9ydGFudCBwcmVkaWN0b3Igb2Ygd2FnZSBsZXZlbC4gSXQgaXMgYWxzbyBsaWtlbHkgdG8gYmUgYSAqY29tbW9uIGNhdXNlIGNvbmZvdW5kZXIqIGZvciB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZWR1Y2F0aW9uIGFuZCB3YWdlcywgc2luY2UgcGVvcGxlIHdpdGggaGlnaGVyIGVkdWNhdGlvbiBsZXZlbHMgbWlnaHQgYmUgbW9yZSBsaWtlbHkgdG8gd29yayBpbiBoaWdoZXIgcGFpZCBzZWN0b3JzIGFuZCBvY2N1cGF0aW9ucyAoaXQgZXNwZWNpYWxseSBob2xkcyB0cnVlIGZvciBtYW5hZ2VtZW50IGFuZCB0ZWNobmljYWwgZ3JvdXBzIHRoYXQgbWlnaHQgcmVxdWlyZSBhZHZhbmNlZCBsZXZlbCBlZHVjYXRpb24pLg0KDQoqKkV0aG5pYyBncm91cHMqKg0KDQpMZXQncyBub3cgb2JzZXJ2ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHdhZ2VzIGFjcm9zcyBkaWZmZXJlbnQgZGVtb2dyYXBoaWMgZ3JvdXBzLg0KDQpgYGB7cn0NCkNQUyA8LSBDUFMgJT4lDQogIG11dGF0ZShldGhuaWNpdHkgPSBmY3RfcmVvcmRlcihldGhuaWNpdHksIHdhZ2UsIC5mdW4gPSBtZWRpYW4pKQ0KDQp3YWdlX2J5X2V0aG5pY2l0eSA8LSBnZ3Bsb3QoQ1BTLCBhZXMoeCA9IGV0aG5pY2l0eSwgeSA9IHdhZ2UpKSArDQogIGdlb21fYm94cGxvdChmaWxsID0gInN0ZWVsYmx1ZSIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjcpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgV2FnZXMgQWNyb3NzIEV0aG5pYyBHcm91cHMiLA0KICAgICAgIHggPSAiT2NjdXBhdGlvbiIsDQogICAgICAgeSA9ICJXYWdlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90bHkod2FnZV9ieV9ldGhuaWNpdHksIHdpZHRoID0gODAwLCBoZWlnaHQgPSA2MDApDQpgYGANCg0KYGBge3J9DQpzdW1tYXJ5X2V0aG5pY2l0eSA8LSBDUFMgJT4lDQogIGdyb3VwX2J5KGV0aG5pY2l0eSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBNZWFuID0gbWVhbih3YWdlKSwNCiAgICBNZWRpYW4gPSBtZWRpYW4od2FnZSksDQogICAgU0QgPSBzZCh3YWdlKSwNCiAgICBJUVIgPSBJUVIod2FnZSwgbmEucm0gPSBUUlVFKSwNCiAgICBDb3VudCA9IG4oKQ0KICApDQoNCnN1bW1hcnlfZXRobmljaXR5DQpgYGANCkNhdWNhc2lhbnMgaGF2ZSBib3RoIHRoZSBoaWdoZXN0IG1lZGlhbiB3YWdlIGFuZCB0aGUgaGlnaGVzdCB2YXJpYWJpbGl0eSBpbiB3YWdlcy4NCg0KSGlnaGVyIHZhcmlhYmlsaXR5IGNhbiBiZSBleHBsYWluZWQgYnkgdGhlIGZhY3QgdGhhdCB0aGUgbWFqb3JpdHkgb2YgdGhlIHNhbXBsZSBpcyBDYXVjYXNpYW4sIHNvIHRoZSBkaXN0cmlidXRpb24gb2Ygd2FnZXMgaXMgbW9yZSBzcHJlYWQgb3V0LiBBZGRpdGlvbmFsbHksIHRoZXJlIG1pZ2h0IHN0cnVjdHVyYWwgZGlmZmVyZW5jZXMgaW4gaW5jb21lcyBiZXR3ZWVuIGRpZmZlcmVudCBldGhuaWMgZ3JvdXBzLiBGb3IgaW5zdGFuY2UsIG5vbi13aGl0ZSBtaW5vcml0aWVzIGhhdmUgbG93ZXIgYWNjZXNzIHRvIGhpZ2hlciBwYWlkIHNlY3RvcnMgYW5kIG9jY3VwYXRpb25zLCB3aGljaCBjYW4gbGVhZCB0byBsb3dlciB3YWdlcy4NCg0KDQpgYGB7cn0NCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KDQpldGhuaWNpdHlfY29sb3JzIDwtIGMoImhpc3BhbmljIiA9ICJicm93bjQiLCAib3RoZXIiID0gImRhcmtnb2xkZW5yb2QiLCAiY2F1YyIgPSAiY2FkZXRibHVlIikNCg0KZXRobmljaXR5X29jY3VwYXRpb25fcGxvdCA8LSBnZ3Bsb3QoQ1BTLCBhZXMoeCA9IG9jY3VwYXRpb24sIGZpbGwgPSBldGhuaWNpdHkpKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArDQogIGxhYnModGl0bGUgPSAiRXRobmljIENvbXBvc2l0aW9uIEFjcm9zcyBPY2N1cGF0aW9ucyIsDQogICAgICAgeCA9ICJPY2N1cGF0aW9uIiwNCiAgICAgICB5ID0gIlByb3BvcnRpb24iLA0KICAgICAgIGZpbGwgPSAiRXRobmljaXR5IikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGV0aG5pY2l0eV9jb2xvcnMpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCg0KZ2dwbG90bHkoZXRobmljaXR5X29jY3VwYXRpb25fcGxvdCkNCmBgYA0KDQpBcyB3ZSBjYW4gc2VlLCBhbW9uZyBvY2N1cGF0aW9uIGdyb3Vwcywgbm9uLWNhdXNhc2lhbnMnIHByb3BvcnRpb25zIGFyZSB0aGUgaGlnaGVzdCBpbiB0aGUgb2ZmaWNlLCB3b3JrZXIgYW5kIHNlcnZpY2VzIG9jY3VwYXRpb24gZ3JvdXBzLCB3aGljaCBhcmUgYXNzb2NpYXRlZCB3aXRoIGxvd2VyIHdhZ2VzLCBhcyB3ZSBzYXcgcHJldmlvdXNseS4NCiANCkdlbmVyYWxseSBzcGVha2luZywgZXRobmljIG9yaWdpbiBtaWdodCBiZSBhIHZlcnkgaW1wb3J0YW50IHByZWRpY3RvciBvZiBhIHdhZ2UgbGV2ZWwuIEFkZGl0aW9uYWxseSwgaXQgbWlnaHQgYWxzbyBhZmZlY3QgdGhlIGFjY2VzcyB0byBlZHVjYXRpb24sIHdoaWNoIG1lYW5zIHRoYXQgaXQgaXMgYSAqY29tbW9uIGNhdXNlIGNvbmZvdW5kZXIqLg0KDQoqKkdlbmRlcioqDQoNCkxldCdzIG9ic2VydmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB3YWdlcyBhY3Jvc3MgZ2VuZGVyLiBHZW5kZXIgZGlzcGFyaXR5IGlzIHdpZGVseSBkaXNjdXNzZWQgaW4gdGhlIGVjb25vbWljIGxpdGVyYXR1cmUsIHNvIGl0IG1pZ2h0IGJlIGludGVyZXN0aW5nIHRvIHNlZSBpZiB0aGVyZSBpcyBhIHdhZ2UgZ2FwIGJldHdlZW4gdHdvIGdlbmRlcnMgaW4gdGhpcyBkYXRhc2V0LiBXZSB3b3VsZCBleHBlY3QgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlLCBlc3BlY2lhbGx5IHNpbmNlIHRoZSBkYXRhIGNvbWVzIGZyb20gODAncyB3aGVuIGdlbmRlciBwYXkgZ2FwIHdhcyBldmVuIG1vcmUgcHJvbm91bmNlZC4NCg0KYGBge3J9DQp3YWdlX2J5X2dlbmRlciA8LSBnZ3Bsb3QoQ1BTLCBhZXMoeCA9IGdlbmRlciwgeSA9IHdhZ2UpKSArDQogIGdlb21fYm94cGxvdChmaWxsID0gInN0ZWVsYmx1ZSIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjcpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgV2FnZXMgQWNyb3NzIEdlbmRlciBHcm91cHMiLA0KICAgICAgIHggPSAiR2VuZGVyIiwNCiAgICAgICB5ID0gIldhZ2UiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpnZ3Bsb3RseSh3YWdlX2J5X2dlbmRlciwgd2lkdGggPSA4MDAsIGhlaWdodCA9IDYwMCkNCmBgYA0KDQpFdmVuIHRob3VnaCBib3RoIG1lZGlhbiBhbmQgaW50ZXJxdWFydGlsZSByYW5nZXMgYXJlIGhpZ2hlciBmb3IgbWFsZXMsIHRoZXJlIGlzIHF1aXRlIGEgbG90IG9mIG91dGxpZXJzIGluIHRoZSBmZW1hbGUgZ3JvdXBzLCBpbmRpY2F0aW5nIGEgc21hbGwgbnVtYmVyIG9mIGhpZ2ggZWFybmVycyBhbW9uZyBmZW1hbGVzLg0KDQpgYGB7cn0NCnN1bW1hcnlfZ2VuZGVyIDwtIENQUyAlPiUNCiAgZ3JvdXBfYnkoZ2VuZGVyKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE1lYW4gPSBtZWFuKHdhZ2UpLA0KICAgIE1lZGlhbiA9IG1lZGlhbih3YWdlKSwNCiAgICBTRCA9IHNkKHdhZ2UpLA0KICAgIElRUiA9IElRUih3YWdlLCBuYS5ybSA9IFRSVUUpLA0KICAgIENvdW50ID0gbigpDQogICkNCg0Kc3VtbWFyeV9nZW5kZXINCmBgYA0KRGVzcGl0ZSBvdXRsaWVycyBtZW50aW9uZWQgZWFybGllciwgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBhbmQgaW50ZXJxdWFydGlsZSByYW5nZSBvZiB0aGUgbWFsZSBncm91cCBzaWduYWwgaGlnaGVyIHZhcmlhYmlsaXR5IGluIHdhZ2VzLg0KDQpBZ2Fpbiwgd2Ugd291bGQgZXhwZWN0IGdlbmRlciB0byBiZSBhICpjb21tb24gY2F1c2UgY29uZm91bmRlciogZm9yIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBlZHVjYXRpb24gYW5kIHdhZ2VzLCBiZWNhdXNlIGl0IGNhbiBhZmZlY3QgYm90aCB0aGUgYWNjZXNzIHRvIGVkdWNhdGlvbiwgbnVtYmVyIG9mIHllYXJzIG9mIGZvcm1hbCBlZHVjYXRpb24gKGR1ZSB0byBkaWZmZXJlbnQgc29jaWFsIG5vcm1zIGFuZCBleHBlY3RhdGlvbnMgYXBwbGllZCB0byBmZW1hbGVzKSBhbmQgdGhlIHdhZ2UgbGV2ZWwgKGR1ZSB0byBnbGFzcyBjZWlsaW5ncykuDQoNCipMb2NhdGlvbioNCg0KVGhlIGRhdGEgYWxzbyBhbGxvd3MgdG8gb2JzZXJ2ZSB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiByZWdpb25zIGluIHRlcm1zIG9mIHdhZ2VzLiBUaGUgZGF0YXNldCBpbmNsdWRlcyAyIGNhdGVnb3JpZXMgcmVsYXRlZCB0byBsb2NhdGlvbjogc291dGggYW5kIG90aGVyLiBXZSB3b3VsZCBleHBlY3QgdGhhdCB0aGVyZSBpcyBhIHdhZ2UgZ2FwIGJldHdlZW4gdGhlc2UgdHdvIHJlZ2lvbnMsIHNpbmNlIHRoZSBTb3V0aCBpcyBnZW5lcmFsbHkgY29uc2lkZXJlZCB0byBiZSBtb3JlIHJ1cmFsLCBsZXNzIGRldmVsb3BlZCwgcmVzdWx0aW5nIGluIGxvd2VyIGF2ZXJhZ2Ugd2FnZXMuDQoNCmBgYHtyfQ0Kd2FnZV9ieV9yZWdpb24gPC0gZ2dwbG90KENQUywgYWVzKHggPSByZWdpb24sIHkgPSB3YWdlKSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbCA9ICJzdGVlbGJsdWUiLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC43KSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFdhZ2VzIEFjcm9zcyBSZWdpb25zIiwNCiAgICAgICB4ID0gIlJlZ2lvbiIsDQogICAgICAgeSA9ICJXYWdlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90bHkod2FnZV9ieV9yZWdpb24sIHdpZHRoID0gODAwLCBoZWlnaHQgPSA2MDApDQpgYGANCg0KSXQncyB2aXZpZGx5IHZpc2libGUgdGhhdCBib3RoIHRoZSBtZWRpYW4gYW5kIHRoZSByYW5nZSBhcmUgaGlnaGVyIGluIHRoZSAib3RoZXIiIGNhdGVnb3J5LCB3aGljaCBpcyBjb25zaXN0ZW50IHdpdGggdGhlIGFzc3VtcHRpb24gdGhhdCB0aGUgU291dGggaGFzIGxvd2VyIHdhZ2VzLg0KDQpgYGB7cn0NCnN1bW1hcnlfcmVnaW9uIDwtIENQUyAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE1lYW4gPSBtZWFuKHdhZ2UpLA0KICAgIE1lZGlhbiA9IG1lZGlhbih3YWdlKSwNCiAgICBTRCA9IHNkKHdhZ2UpLA0KICAgIElRUiA9IElRUih3YWdlLCBuYS5ybSA9IFRSVUUpLA0KICAgIENvdW50ID0gbigpDQogICkNCg0Kc3VtbWFyeV9yZWdpb24NCmBgYA0KKlVuaW9uaXphdGlvbioNCg0KVW5pb25pemF0aW9uIGlzIGFub3RoZXIgaW1wb3J0YW50IGZhY3RvciB0aGF0IGNhbiBhZmZlY3Qgd2FnZXM6IHVuaW9uIG1lbWJlciBhcmUgcHJvdGVjdGVkIGJ5IHRoZSBjb2xsZWN0aXZlIGFncmVlbWVudHMgYW5kIHVzdWFsbHkgaGF2ZSBoaWdoZXIgd2FnZXMuDQoNCmBgYHtyfQ0Kd2FnZV9ieV91bmlvbiA8LSBnZ3Bsb3QoQ1BTLCBhZXMoeCA9IHVuaW9uLCB5ID0gd2FnZSkpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAic3RlZWxibHVlIiwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNykgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBXYWdlcyBBY3Jvc3MgVW5pb24gTWVtYmVyc2hpcHMiLA0KICAgICAgIHggPSAiVW5pb24gTWVtYmVyc2hpcCIsDQogICAgICAgeSA9ICJXYWdlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90bHkod2FnZV9ieV91bmlvbiwgd2lkdGggPSA4MDAsIGhlaWdodCA9IDYwMCkNCmBgYA0KDQoNCg0KIyMgQ2F1c2FsIEFuYWx5c2lzDQoNCkFmdGVyIHByZWxpbWluYXJ5IGNvcnJlbGF0aW9uIGFuZCBleHBsb3JhdG9yeSBhbmFseXNpcywgd2UgY2FuIG5vdyBwcm9jZWVkIHdpdGggdGhlIGh5cG90aGVzaXMgdGVzdGluZy4gRWR1Y2F0aW9uIHdhcyBmb3VuZCB0byBoYXZlIHRoZSBoaWdoZXN0IHBvc2l0aXZlIGNvcnJlbGF0aW9uIHdpdGggd2FnZXMgYW1vbmcgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyAoY29ycmVsYXRpb25zIGJldHdlZW4gd2FnZXMgYW5kIGFnZSBhbmQgZXhwZXJpZW5jZSB3ZXJlIHdlYWtlcikuIFF1YWxpdGF0aXZlIHZhcmlhYmxlcywgc3VjaCBhcyBvY2N1cGF0aW9uIGFuZCBzZWN0b3IsIGFsc28gc2hvd2VkIHNvbWUgZGlmZmVyZW5jZXMgaW4gd2FnZSBkaXN0cmlidXRpb24gYW5kIHRoZWlyIHJlbGF0aW9uc2hpcCB3aXRoIHdhZ2VzIG1pZ2h0IGJlIGludGVyZXN0aW5nIHRvIGV4cGxvcmUgZnVydGhlci4NCg0KVGhlIHNldCBvZiBoeXBvdGhlc2VzIGlzIGZvcm11bGF0ZWQgaW4gdGhlIGZvbGxvd2luZyB3YXk6DQoNCi0gKk51bGwgSHlwb3RoZXNpcyAoSOKCgCk6IEVkdWNhdGlvbiBoYXMgbm8gZWZmZWN0IG9uIHdhZ2VzLioNCi0gKkFsdGVybmF0aXZlIEh5cG90aGVzaXMgKEjigoEpOiBFZHVjYXRpb24gaGFzIGFuIGVmZmVjdCBvbiB3YWdlcy4qDQoNCg0KTGV0J3MgaGF2ZSBhIGxvb2sgYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBsZXZlbCBvZiBlZHVjYXRpb24sIGNvbnRyb2xsaW5nIGZvciBleHBlcmllbmNlIGFuZCBhZ2UsIGFuZCB3YWdlcy4NCg0KRmlyc3QsIHNpbmNlIGFnZSBhbmQgZXhwZXJpZW5jZSBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQsIHdlIHdpbGwgY2hlY2sgZm9yICptdWx0aWNvbGxpbmVhcml0eSogYmV0d2VlbiB0aGVzZSB0d28gdmFyaWFibGVzLCB1c2luZyAqVmFyaWFuY2UgSW5mbGF0aW9uIEZhY3RvciAoVklGKSouIExldCdzIGNyZWF0ZSBtb2RlbHMgd2l0aCBhbmQgd290aG91dCBjb2xsaW5lYXIgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGFuZCBjb21wYXJlIHRoZSByZXN1bHRzOg0KDQpgYGB7cn0NCiMgaW5zdGFsbC5wYWNrYWdlcygic3RhcmdhemVyIikNCmxpYnJhcnkoc3RhcmdhemVyKQ0KDQptb2RlbF9udW1lcmljYWxfMSA8LSBsbSh3YWdlIH4gZWR1Y2F0aW9uICsgZXhwZXJpZW5jZSArIGFnZSwgZGF0YSA9IENQUykNCm1vZGVsX251bWVyaWNhbF8yIDwtIGxtKHdhZ2UgfiBlZHVjYXRpb24gKyBleHBlcmllbmNlLCBkYXRhID0gQ1BTKQ0KbW9kZWxfbnVtZXJpY2FsXzMgPC0gbG0od2FnZSB+IGVkdWNhdGlvbiArIGFnZSwgZGF0YSA9IENQUykNCg0Kc3RhcmdhemVyKG1vZGVsX251bWVyaWNhbF8xLCBtb2RlbF9udW1lcmljYWxfMiwgbW9kZWxfbnVtZXJpY2FsXzMsDQogICAgICAgICAgdHlwZSA9ICJ0ZXh0IiwNCiAgICAgICAgICB0aXRsZSA9ICJXYWdlIFZzLiBFZHVjYXRpb24sIEV4cGVyaWVuY2UgJiBBZ2UiLA0KICAgICAgICAgIGFsaWduID0gVFJVRSwNCiAgICAgICAgICBjb2x1bW4ubGFiZWxzID0gYygiQWxsIG51bWVyaWNhbCIsICJ3L28gQWdlIiwgIncvbyBFeHBlcmllbmNlIikpDQpgYGANCkZpcnN0IG1vZGVsIGluY2x1ZGVzIGFsbCB0aHJlZSB2YXJpYWJsZXMsIHdoaWxlIHRoZSBzZWNvbmQgbW9kZWwgaW5jbHVkZXMgb25seSBlZHVjYXRpb24gYW5kIGV4cGVyaWVuY2UgKGV4Y2x1ZGluZyBhZ2UpLiBUaGUgdGhpcmQgbW9kZWwgaW5jbHVkZXMgb25seSBlZHVjYXRpb24gYW5kIGFnZSAoZXhjbHVkaW5nIGV4cGVyaWVuY2UpLg0KDQpGaXJzdCBtb2RlbCBkZW1vbnN0cmF0ZXMgaGlnaGx5IGluZmxhdGVkIHN0YW5kYXJkIGVycm9ycywgbW9zdCBsaWtlbHkgZHVlIHRvIG11bHRpY29sbGluZWFyaXR5IGJldHdlZW4gZXhwZXJpZW5jZSBhbmQgYWdlLg0KDQpCb3RoIDJuZCBhbmQgMnJkIG1vZGVscyBzaG93IGFuZCBzdGF0aXN0aWNhbCBhbmQgcXVpdGUgaGlnaCBjb3JyZWxhdGlvbiBiZXR3ZWVuIGVkdWNhdGlvbiBhbmQgd2FnZSwgd2hpbGUgdGhlIHJlc3BlY3RpdmUgY29lZmZpY2llbnRzIGZvciBpc29sYXRlZCBleHBlcmllbmNlIGFuZCBhZ2UgdmFyaWFibGVzIGFyZSBxdWl0ZSBzaW1pbGFyLiBpdCBpcyBpbmRlZWQgYSBzaWduIG9mIG11bHRpY29sbGluZWFyaXR5Lg0KDQpMZXQncyBzZWUgdGhlIFZhcmlhbmNlIEluZmxhdGlvbiBGYWN0b3IgdG8gY2hlY2sgdGhlIHBvd2VyIG9mIG11bHRpY29sbGluZWFyaXR5IGluIHRoZSAxc3QgbW9kZWw6DQoNCmBgYHtyfQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJjYXIiKQ0KbGlicmFyeShjYXIpDQoNCnZpZjEgPC0gdmlmKG1vZGVsX251bWVyaWNhbF8xKQ0KcHJpbnQodmlmMSkNCmBgYA0KVGhlIGNhbGN1bGF0ZWQgVklGIGlzIGV4dHJlbWVseSBoaWdoLCB3aGljaCBpbmRpY2F0ZXMgdGhhdCBtdWx0aWNvbGxpbmVhcml0eSBpcyBhIHNpZ25pZmljYW50IGlzc3VlIGluIHRoZSBtb2RlbC4NCg0KTGV0J3MgY2hlY2sgVklGIGZvciB0aGUgMm5kIGFuZCAzcmQgbW9kZWxzOg0KYGBge3J9DQp2aWYyIDwtIHZpZihtb2RlbF9udW1lcmljYWxfMikNCnByaW50KHZpZjIpDQpgYGANCmBgYHtyfQ0KdmlmMyA8LSB2aWYobW9kZWxfbnVtZXJpY2FsXzMpDQpwcmludCh2aWYzKQ0KYGBgDQpCb3RoIG1vZGVscyBoYXZlIFZJRiB2YWx1ZXMgfjEsIHdoaWNoIGlzIGEgc2lnbiBvZiBubyBtdWx0aWNvbGxpbmVhcml0eS4NCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoInN0YXJnYXplciIpDQpsaWJyYXJ5KHN0YXJnYXplcikNCg0KbW9kZWxfY2F0ZWdvcmljYWwgPC0gbG0od2FnZSB+IGV0aG5pY2l0eSArIHJlZ2lvbiArIGdlbmRlciArIG9jY3VwYXRpb24gKyBzZWN0b3IgDQorIHVuaW9uICsgbWFycmllZCwgZGF0YSA9IENQUykNCg0Kc3RhcmdhemVyKG1vZGVsX2NhdGVnb3JpY2FsLCB0eXBlID0gInRleHQiLCB0aXRsZSA9ICJXYWdlIFZzLiBDYXRlZ29yaWNhbCB2YXJpYWJsZXMiLCBhbGlnbiA9IFRSVUUpDQpgYGANCmBgYHtyfQ0KbW9kZWxfbnVtZXJpY2FsIDwtIGxtKHdhZ2UgfiBlZHVjYXRpb24gKyBleHBlcmllbmNlICsgYWdlLCBkYXRhID0gQ1BTKQ0KDQpzdGFyZ2F6ZXIobW9kZWxfbnVtZXJpY2FsLCB0eXBlID0gInRleHQiLCB0aXRsZSA9ICJSZWdyZXNzaW9uIFJlc3VsdHMiLCBhbGlnbiA9IFRSVUUpDQpgYGANCkRvIG91dGxpZXJzIGFmZmVjdCB0aGUgcmVzdWx0cz8NCi0gQ29vaydzIERpc3RhbmNlIA0KLSBSb2J1c3RuZXNzIGNoZWNrcw0KDQoNCiMjIExpbWl0YXRpb25zDQoNCi0geSBpcyBub3QgdHJ1bHkgcmFuZG9tDQoNCg==